package auth;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.core.oidc.OidcScopes;
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.settings.ClientSettings;
import org.springframework.security.oauth2.server.authorization.settings.OAuth2TokenFormat;
import org.springframework.security.oauth2.server.authorization.settings.TokenSettings;
import org.springframework.util.AlternativeJdkIdGenerator;
import org.springframework.util.IdGenerator;

import java.time.Duration;

@SpringBootApplication
public class AuthApplication {
    public static void main(String[] args) {
        SpringApplication.run(AuthApplication.class, args);
    }

    // @Bean
    ApplicationListener<ApplicationReadyEvent> initRegisteredClient() {
        IdGenerator idGenerator = new AlternativeJdkIdGenerator();
        return event -> {
            ConfigurableApplicationContext context = event.getApplicationContext();
            RegisteredClientRepository clientRepository = context.getBean(RegisteredClientRepository.class);

            RegisteredClient registeredClient = RegisteredClient
                    .withId(idGenerator.generateId().toString().replaceAll("-", ""))
                    .clientName("spring-cloud-gateway-client-oidc")
                    .clientId("gateway")
                    .clientSecret("{noop}gateway")
                    .clientAuthenticationMethods(clientAuthenticationMethods -> {
                        // https://docs.spring.io/spring-security/reference/reactive/oauth2/client/authorization-grants.html#_initiating_the_authorization_request
                        clientAuthenticationMethods.add(ClientAuthenticationMethod.NONE);
                        clientAuthenticationMethods.add(ClientAuthenticationMethod.CLIENT_SECRET_POST);
                        clientAuthenticationMethods.add(ClientAuthenticationMethod.CLIENT_SECRET_BASIC);
                    })
                    .authorizationGrantTypes(authorizationGrantTypes -> {
                        authorizationGrantTypes.add(AuthorizationGrantType.REFRESH_TOKEN);
                        authorizationGrantTypes.add(AuthorizationGrantType.AUTHORIZATION_CODE);
                    })
                    .redirectUris(redirectUris -> {
                        redirectUris.add("http://127.0.0.1:9527/login/oauth2/code/gateway-client-oidc");
                    })
                    .scopes(scopes -> {
                        scopes.add(OidcScopes.OPENID);
                        scopes.add(OidcScopes.PROFILE);
                    })
                    .clientSettings(ClientSettings.builder()
                            .requireAuthorizationConsent(false) // 是否需要授权同意
                            .requireProofKey(false) // 是否仅支持 PKCE 登录(PKCE 不要设置 clientSecret 且 ClientAuthenticationMethod 为 NONE)
                            .build()
                    )
                    .tokenSettings(TokenSettings.builder()
                            .accessTokenFormat(OAuth2TokenFormat.SELF_CONTAINED) // 生成 JWT token（还有 opaque token）
                            .idTokenSignatureAlgorithm(SignatureAlgorithm.RS256) // 签名算法
                            .accessTokenTimeToLive(Duration.ofSeconds(30 * 60)) // access_token 有效期
                            .refreshTokenTimeToLive(Duration.ofSeconds(60 * 60)) // refresh_token 有效期
                            .reuseRefreshTokens(true) // 是否重用刷新令牌
                            .build()
                    )
                    .build();

            clientRepository.save(registeredClient);
        };
    }
}